import { Draw } from "../Draw/Draw";
import { Command } from "../Command/Command";
import { IKeyMap, VoidFun } from "../Interface/Pptx";
import { ISlideAnimation } from "../Interface/Animation";
import { SlideAnimationEnum } from "../Enumerate/SlideAnimation";
import { submenuTemp, TopMenuTemp } from "../Template/TopMenuTemp";
import { setStageCursor } from "../Event/handle/konva/stage-cursor";
import { GENERAL_INSERT_TYPE } from "../Config/general-insert-type";

/**
 * 顶部菜单类
 */
export class TopMenu {
  private draw: Draw;
  private command: Command;

  constructor(draw: Draw) {
    this.draw = draw;
    this.command = draw.getCommand();

    /** 创建 top menu 菜单，添加到 konva-root 中 */
    const root = this.draw.getRootBox();
    const TopMenu = document.createElement("div");
    TopMenu.className = "konva-root-topmenu";
    TopMenu.innerHTML = TopMenuTemp;
    root.appendChild(TopMenu);

    /** 初始化事件（一级菜单点击事件） */
    const menulistNodeList = TopMenu.querySelectorAll("[data-menu]")!;
    const menulist = <NodeListOf<HTMLElement>>menulistNodeList;

    // 单独处理 filename 文件重命名 - 重命名协同实现
    const filenameSelector = ".konva-root-topmenu-filename";
    const filename = <HTMLInputElement>TopMenu.querySelector(filenameSelector);
    filename.addEventListener("blur", this.fileNameChange.bind(this));

    /** 循环遍历 添加 click 事件，有些菜单是直接执行 command 有些是需要打开二级菜单 */
    menulist.forEach((menu) =>
      menu.addEventListener("click", (e) => {
        e.stopPropagation();
        e.preventDefault();
        this.topMenuEventHandle.call(this, menu);
      })
    );
  }

  /**
   * 一级菜单事件处理函数(只是打开二级菜单这些事项，真正的执行函数都在 Command API 中)
   */
  private topMenuEventHandle(menu: HTMLElement) {
    // 解析菜单类型
    const menuType = menu.dataset.menu;

    // 定义事件映射
    const eventMap: IKeyMap<string, () => void> = {
      "topmenu-file": this.file.bind(this, menu), // 打开文件菜单
      "topmenu-add-slide": this.command.executeAddSlide, // 新建幻灯片
      "slide-temp": this.command.executeOpenSlideTemp, // 新建模板
      "topmenu-save": this.command.executeSave, // 保存
      "topmenu-undo": this.command.executeUndo, // 撤销
      "topmenu-redo": this.command.executeRedo, // 重做
      "topmenu-insert": this.insert.bind(this, menu), // 插入
      "topmenu-desigh": () => this.draw.getShapeSettings().open(), // 设计
      "topmenu-switch": this.switch.bind(this, menu), // 切换
      "topmenu-animation": this.command.executeAnimation, // 动画
      "topmenu-play": this.play.bind(this, menu), // 放映
      "topmenu-view": this.view.bind(this, menu), // 视图
      "topmenu-share": this.command.executeShare, // 分享
      "topmenu-message": this.command.executeOpenMessage, // 消息中心
    };

    // 执行事件
    if (menuType && eventMap[menuType]) eventMap[menuType]();
  }

  /**
   * 文件重命名
   */
  private fileNameChange(e: FocusEvent) {
    const filename = (<HTMLElement>e.target).innerText;
    // 将当前文件名称广播给其他客户端
    this.draw.broadcast({ type: "_filename", payload: { filename } });
  }

  /**
   * 文件菜单项
   * @param menu
   */
  private file(menu: HTMLElement) {
    this.draw.getMenuMask().createMask()
    const submenuBox = this.createSubMenu(submenuTemp.filemenuTemp);
    submenuBox.style.width = "220px";
    submenuBox.style.left = "0";
    submenuBox.style.transform = "translate(0, 0)";
    menu.appendChild(submenuBox);

    // 这里还需要监听这个菜单下面的事件
    const submenus = submenuBox.querySelectorAll("[data-submenu]");
    const submenuList = <NodeListOf<HTMLElement>>submenus;

    const eventMap: IKeyMap<string, VoidFun> = {
      importpptx: this.command.executeImportPptx,
      exportpptx: this.command.executeExportPptx,
      exportpdf: this.command.executeExportPdf,
      exportimage: this.command.executeExportImage,
      share: this.command.executeShare,
      encrypt: this.command.executeEncrypt,
      print: this.command.executePrint,
      shortcut: () => this.command.executeHelp("shortcut"),
      question: () => this.command.executeHelp("question"),
      feedback: () => this.command.executeHelp("feedback"),
    };

    submenuList.forEach((submenu) => {
      const subMenuType = submenu.dataset.submenu!;
      submenu.addEventListener("click", (e) => {
        if (eventMap && eventMap[subMenuType]) eventMap[subMenuType].call(this);
        // 点击完成后，需要关闭当前菜单
        this.draw.getMenuMask().removeMask();
        e.stopPropagation();
        e.preventDefault();
      });
    });
  }

  /**
   * 插入菜单项
   * @param menu
   */
  private insert(menu: HTMLElement) {
    this.draw.getMenuMask().createMask()
    const submenuBox = this.createSubMenu(submenuTemp.insertTemp);
    submenuBox.style.width = "260px";
    submenuBox.style.height = "440px";
    menu.appendChild(submenuBox);

    const stage = this.draw.getStage();

    const textHandle = (insertShape: string) => {
      setStageCursor(this.draw, "text");
      stage.setAttrs({ insertShape });
    };

    // 事件处理函数 - 看能不能合成一个处理函数，通过识别不同的形状实现
    const eventMap: IKeyMap<string, VoidFun> = {
      text1: () => textHandle("text1"),
      // 下列插入需要执行 command 方法
      text2: this.command.executeInsertRichText,
      chart: this.command.executeInsertChart,
      media: this.command.executeInsertMedia,
      table: this.command.executeInsertTable,
      image: this.command.executeInsertImage,
      latex: this.command.executeInsertLatex,
    };

    // 这里需要添加的菜单，需要监听点击事件
    const insertmenus = submenuBox.querySelectorAll("[data-insert]");
    const insertMenuList = <NodeListOf<HTMLElement>>insertmenus;

    insertMenuList.forEach((menu) => {
      const insertType = menu.dataset.insert!;
      menu.addEventListener("click", (e) => {
        setStageCursor(this.draw, "crosshair");

        // 普通事件处理列表
        if (GENERAL_INSERT_TYPE.includes(insertType))
          stage.setAttrs({ insertShape: insertType });

        // 特殊事件处理函数
        if (insertType && eventMap[insertType]) eventMap[insertType].call(this);

        // 点击完成后，需要关闭当前菜单
        this.draw.getMenuMask().removeMask();

        // 判断当前是否存在layer
        const layer = this.draw.getLayer();
        if (!layer) this.draw.addSlide();

        e.stopPropagation();
        e.preventDefault();
      });
    });
  }

  /**
   * 切换菜单项
   * @param menu
   */
  private switch(menu: HTMLElement) {
    this.draw.getMenuMask().createMask()
    const submenuBox = this.createSubMenu(submenuTemp.switchTemp);
    submenuBox.style.width = "480px";
    submenuBox.style.height = "auto";
    menu.appendChild(submenuBox);

    // slideAnimation
    const slideAnimation = this.draw.getSlideAnimation();
    const layer = this.draw.getLayer();
    const currentAnimatio = layer?.getAttr("animation");

    const animations = submenuBox.querySelectorAll("[data-switch]");
    const animationList = <NodeListOf<HTMLElement>>animations;

    type ICAP = { global: boolean };
    const setAnimation = (p: ISlideAnimation) => slideAnimation.setAnimation(p);
    const cancelAnimation = (p?: ICAP) => slideAnimation.cancelAnimation(p);

    /** 每一个切换动画项点击事件回调 */
    function clickHandle(switchItem: HTMLElement) {
      const animatioType = switchItem.dataset.switch!;

      // 处理激活样式
      if (animatioType !== "apply") {
        animationList.forEach((item) => item.classList.remove("active"));
        switchItem.classList.add("active");
      }

      switch (animatioType) {
        case "apply":
          // 应用全部 - 需要解析animation的类型
          animationList.forEach((item) => {
            if (!item.classList.contains("active")) return;
            // 解析它的类型
            const type = item.dataset.switch!;
            if (type === "none") cancelAnimation({ global: true });
            else setAnimation({ name: <SlideAnimationEnum>type, global: true });
          });
          break;
        case "none":
          cancelAnimation();
          break;

        default:
          // 其他情况下点击都是直接应用动画即可
          setAnimation({ name: <SlideAnimationEnum>animatioType });
          break;
      }
    }

    // 给每一个 animation 添加事件处理函数
    animationList.forEach((switchItem) => {
      // 解析每一个动画类型
      const animatioType = switchItem.dataset.switch!;
      // 每一项都移除 active 样式
      switchItem.classList.remove("active");

      // 拿当前 layer 动画，处理激活样式
      const setActive = () => switchItem.classList.add("active");

      if (currentAnimatio) {
        if (animatioType === currentAnimatio) setActive();
      } else {
        if (animatioType === "none") setActive();
      }

      switchItem.addEventListener("click", clickHandle.bind(this, switchItem));
    });
  }

  /**
   * 放映菜单项
   */
  private play(menu: HTMLElement) {
    this.draw.getMenuMask().createMask()
    const submenuBox = this.createSubMenu(submenuTemp.playTemp);
    submenuBox.style.width = "220px";
    submenuBox.style.height = "auto";
    menu.appendChild(submenuBox);

    const eventMap: IKeyMap<string, VoidFun> = {
      start: this.command.executePreviewBeginning,
      current: this.command.executePreviewCurrent,
      record: this.command.executeRecord,
    };

    // 获取菜单下的按钮
    const menus = submenuBox.querySelectorAll("[data-play]");
    const menuList = <NodeListOf<HTMLElement>>menus;
    menuList.forEach((menu) => {
      const playType = menu.dataset.play!;
      menu.addEventListener("click", (e) => {
        if (eventMap && eventMap[playType]) eventMap[playType].call(this);
        this.draw.getMenuMask().removeMask();
        e.stopPropagation();
        e.preventDefault();
      });
    });
  }

  /**
   * 视图菜单项
   */
  private view(menu: HTMLElement) {
    this.draw.getMenuMask().createMask()
    // 需要确认当前状态是否已经标记了 网格线及标尺
    const line = this.draw.getLayerManager().getGridLine();
    const payload = { line, ruler: false };
    const submenuBox = this.createSubMenu(submenuTemp.viewTemp(payload));

    submenuBox.style.width = "180px";
    submenuBox.style.height = "auto";
    menu.appendChild(submenuBox);

    // 获取当前的状态
    const layerManager = this.draw.getLayerManager();

    const eventMap: IKeyMap<string, VoidFun> = {
      ruler: () => this.command.executeSetBackground({ ruler: true }), // 标尺
      grid: () => {
        // 网格
        const grid = layerManager.getGridLine();
        this.command.executeSetBackground({ grid: !grid });
      },
    };

    // 获取菜单下的按钮
    const menus = submenuBox.querySelectorAll("[data-view]");
    const menuList = <NodeListOf<HTMLElement>>menus;
    menuList.forEach((menu) => {
      const viewType = menu.dataset.view!;
      menu.addEventListener("click", (e) => {
        if (viewType && eventMap[viewType]) eventMap[viewType].call(this);
        this.draw.getMenuMask().removeMask();
        e.stopPropagation();
        e.preventDefault();
      });
    });
  }

  /** 创建二级菜单结构 */
  private createSubMenu(temp: string) {
    const box = document.createElement("div");
    box.className = "submenu";
    box.innerHTML = temp;
    // 需要阻止事件冒泡
    box.addEventListener("click", (e) => e.stopPropagation());
    return box;
  }
}
